// axios配置  可自行根据项目进行更改，只需更改该文件即可，其他文件可以不动
import { TAxios } from './Axios'
import { AxiosTransform } from './axiosTransform'
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { checkStatus } from './checkStatus'
import { joinTimestamp, formatRequestDate } from './helper'
import { RequestEnum, ResultEnum, ContentTypeEnum } from './httpEnum'
import { isString, deepMerge, isUrl, setObjToUrlParams } from './utils'
import { RequestOptions, Result, CreateAxiosOptions, CreateInstanceOptions, Recordable } from './types'
import { storage } from '@/utils/Storage'
// import { ConsoleSqlOutlined } from '@vicons/antd'

/**
 * @description: 数据处理，方便区分多种处理方式
 */

function createTransform(createConfig: CreateInstanceOptions): AxiosTransform {
  const { messageFn, modalFn, loginTimeoutFn, getTokenFn, refreshTokenFn, loginApiUrl } = createConfig

  return {
    /**
     * @description: 请求成功处理
     */
    transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
      const {
        isShowMessage = true,
        isShowErrorMessage,
        isShowSuccessMessage,
        successMessageText,
        errorMessageText,
        isTransformResponse,
        isReturnNativeResponse
      } = options
      // 是否返回原生响应头 比如：需要获取响应头时使用该属性
      if (isReturnNativeResponse) {
        return res
      }
      // 不进行任何处理，直接返回
      // 用于页面代码可能需要直接获取code，data，message这些信息时开启
      if (!isTransformResponse) {
        return res.data
      }

      if (!res.data) {
        throw new Error('请求出错，请稍候重试')
      }

      // 这里 code，result，message为 后台统一的字段，需要修改为项目自己的接口返回格式
      const { code, data, msg } = res.data
      // 请求成功
      // Reflect Js的一个内置的对象 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
      // Reflect.has(obj, prop) 判断对象是否具有指定的属性
      const hasSuccess = code === ResultEnum.SUCCESS

      if (code === 'A00017' || msg === '令牌已过期') {
        messageFn.error('令牌已过期')
        storage.clear()
        sessionStorage.clear()
        window.location.href = '/login'
      }

      // 是否显示提示信息
      if (isShowMessage) {
        if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
          messageFn.success(successMessageText || msg || '操作成功')
        } else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
          messageFn.error(errorMessageText || msg || '操作失败')
        } else if (!hasSuccess && !isShowErrorMessage) {
          // 是否显示自定义信息提示
          messageFn.error(msg || '操作失败')
        } else if (!hasSuccess && options.errorMessageMode === 'modal') {
          // errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗，而不是消息提示，用于一些比较重要的错误
          modalFn.error(msg)
        }
      }

      // 接口请求成功，直接返回结果
      if (code === ResultEnum.SUCCESS) {
        return data
      }

      // 接口请求错误，统一提示错误信息 这里逻辑可以根据项目进行修改
      let errorMsg = msg
      switch (code) {
        // 请求失败
        case ResultEnum.ERROR:
          messageFn.error(errorMsg)
          break
        // 登录超时
        case ResultEnum.TIMEOUT:
          errorMsg = loginTimeoutFn() || errorMsg
          break
      }
      if (code === ResultEnum.FORM_FEILD_NAME_REPEAT) {
        throw res.data
      } else {
        throw new Error(errorMsg)
      }
    },

    // 请求之前处理config
    beforeRequestHook: (config, options) => {
      const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix, isMock } = options

      // 是否标准的url地址 如：Http://localhost:8080/
      const isUrlStr = isUrl(config.url as string)

      // 请求地址加上前缀
      if (!isUrlStr && joinPrefix) {
        config.url = `${urlPrefix}${config.url}`
      }

      // 请求地址加上api地址
      if (!isUrlStr && apiUrl && isString(apiUrl) && !isMock) {
        config.url = `${apiUrl}${config.url}`
      }

      const params = config.params || {}
      const data = config.data || false

      if (config.method?.toUpperCase() === RequestEnum.GET) {
        // get 请求设置
        if (!isString(params)) {
          // 给 get 请求加上时间戳参数，避免从缓存中拿数据。
          config.params = Object.assign(params || {}, joinTimestamp(joinTime, false))
        } else {
          // 兼容restful风格
          config.url = config.url + params + `${joinTimestamp(joinTime, true)}`
          config.params = undefined
        }
      } else if (!isString(params)) {
        // 参数不是字符串 执行以下操作
        formatDate && formatRequestDate(params)
        // 判断data里面是否有值，如果没有，就用params里面的值
        if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
          config.data = data
          config.params = params
        } else {
          config.data = params
          config.params = undefined
        }
        // joinParamsToUrl 为true 就把请求参数拼接到url上
        if (joinParamsToUrl) {
          config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data))
        }
      } else {
        // 兼容restful风格
        config.url = config.url + params
        config.params = undefined
      }
      return config
    },
    /**
     * @description: 请求拦截器处理
     */
    requestInterceptors(cfg, options) {
      // 请求之前处理config
      let token = getTokenFn()
      const config: Recordable = cfg
      const url = (config as AxiosRequestConfig)?.url || ''
      if (url.includes(loginApiUrl)) {
        token = 'Basic cm9vdDpyb290'
        config.headers.Authorization = token
      } else {
        // 如果token 是 -1 则表示token过期了，需要拿刷新token来刷新
        if (token === '-1') {
          token = refreshTokenFn() as any
          if (!token) {
            throw new Error('token过期了，请重新登录')
          }
        }

        const headers = config.headers || {}

        const appinfo = (window as any)._PUBLIC_UTILS_.getAppInfo()
        const orgInfo = storage.get('ORGINFO')

        config.headers = Object.assign(headers, {
          'yootrial-account': orgInfo ? orgInfo.accountId : '0',
          'yootrial-app': appinfo ? appinfo.appId : '0',
          'yootrial-timestamp': Date.now(),
          'yootrial-signature': '0',
          token
        })

        if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
          // withToken 是否携带token  有token并且config的withToken必须携带的
          ;(config as Recordable).headers.Authorization = options.authenticationScheme
            ? `${options.authenticationScheme}${token}`
            : token
        }
      }
      return config
    },

    /**
     * @description: 响应错误处理
     */
    responseInterceptorsCatch: (error: any) => {
      const { response, code, message } = error || {}

      // TODO 此处要根据后端接口返回格式修改
      const msg: string = response && response.data && response.data.msg ? response.data.msg : ''
      const err: string = error.toString()
      try {
        if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
          messageFn.error('当前网络较差，请稍后重试')
          return
        }
        if (err && err.includes('Network Error')) {
          modalFn.error('请检查您的网络连接是否正常')
          return Promise.reject(error)
        }
      } catch (error) {
        throw new Error(error as any)
      }

      // 如果登录,需要特殊判断,不需要走下面的逻辑
      const { request, data } = response
      if (request.responseURL.includes(loginApiUrl)) {
        messageFn.error(data.msg || '登录异常')
        return Promise.reject(error)
      }

      // 请求是否被取消
      const isCancel = axios.isCancel(error)
      if (!isCancel) {
        checkStatus(error.response && error.response.status, msg, messageFn)
      } else {
        console.warn(error, '请求被取消！')
      }
      //return Promise.reject(error);
      return Promise.reject(response?.data)
    }
  }
}

// Partial type Partial<T> = { [P in keyof T]?: T[P]};
// Partial<CreateAxiosOptions> 类型依然继承自CreateAxiosOptions，通过Partial把CreateAxiosOptions里面的参数都变成可选的
export function createAxios(config: CreateInstanceOptions, opt?: Partial<CreateAxiosOptions>) {
  return new TAxios(
    deepMerge(
      {
        timeout: 10 * 1000,
        authenticationScheme: 'Bearer',
        // 接口前缀
        // prefixUrl: prefixUrl,
        headers: { 'Content-Type': ContentTypeEnum.JSON },
        // 数据处理方式
        transform: createTransform(config),
        // 配置项，下面的选项都可以在独立的接口请求中覆盖
        requestOptions: {
          // 默认将prefix 添加到url
          joinPrefix: true,
          // 是否返回原生响应头 比如：需要获取响应头时使用该属性
          isReturnNativeResponse: false,
          // 需要对返回数据进行处理
          isTransformResponse: true,
          // post请求的时候添加参数到url
          joinParamsToUrl: false,
          // 格式化提交参数时间
          formatDate: true,
          // 消息提示类型
          errorMessageMode: 'none',
          // 接口地址
          apiUrl: config?.apiUrl || '',
          // 接口拼接地址
          urlPrefix: config?.urlPrefix || '',
          //  是否加入时间戳
          joinTime: true,
          // 忽略重复请求
          ignoreCancelToken: true,
          // 是否携带token
          withToken: true
        },
        withCredentials: false
      },
      opt || {}
    )
  )
}

// export const http = createAxios()
